package com.yunxin.admin.system.application.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yunxin.admin.system.application.DeptQueryService;
import com.yunxin.admin.system.application.assembler.DeptAssembler;
import com.yunxin.admin.system.application.command.DeptCommand;
import com.yunxin.admin.system.application.dto.DeptDTO;
import com.yunxin.admin.system.application.dto.DeptInfoDTO;
import com.yunxin.admin.system.application.dto.OptionDTO;
import com.yunxin.admin.system.infra.common.enums.StatusEnum;
import com.yunxin.admin.system.infra.persistence.entity.SysDeptDO;
import com.yunxin.admin.system.infra.persistence.mapper.SysDeptMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author zero°
 * @since 2023-07-11
 */
@Service
@RequiredArgsConstructor
public class DeptQueryServiceImpl implements DeptQueryService {

    private final SysDeptMapper sysDeptMapper;

    private final DeptAssembler deptAssembler;

    @Override
    public List<DeptDTO> listDepartments(DeptCommand command) {
        final String keywords = command.getKeywords();
        final Integer status = command.getStatus();
        // 查询数据
        List<SysDeptDO> results = sysDeptMapper.selectList(new LambdaQueryWrapper<SysDeptDO>()
                .like(CharSequenceUtil.isNotBlank(keywords), SysDeptDO::getName, keywords)
                .eq(SysDeptDO::getBoke, 1)
                .eq(status != null, SysDeptDO::getStatus, status)
                .orderByAsc(SysDeptDO::getSort));
        return converterDTO(results);
    }


    @Override
    public List<OptionDTO<Long>> listDeptOptions() {
        List<OptionDTO<Long>> optionDTOList = new ArrayList<>();
        List<SysDeptDO> deptList = sysDeptMapper.selectList(new LambdaQueryWrapper<SysDeptDO>()
                .eq(SysDeptDO::getStatus, StatusEnum.ENABLE.getValue())
                .eq(SysDeptDO::getBoke, 1)
                .select(SysDeptDO::getId, SysDeptDO::getParentId, SysDeptDO::getName)
                .orderByAsc(SysDeptDO::getSort)
        );
        Set<Long> deptIds = deptList.stream().map(SysDeptDO::getId).collect(Collectors.toSet());
        Set<Long> parentIds = deptList.stream().map(SysDeptDO::getParentId).collect(Collectors.toSet());
        List<Long> rootIds = CollUtil.subtractToList(parentIds, deptIds);
        for (Long rootId : rootIds) {
            optionDTOList.addAll(recurDeptTreeOptions(rootId, deptList));
        }
        return optionDTOList;
    }

    @Override
    public DeptInfoDTO getDeptInfo(Long deptId) {
        SysDeptDO sysDeptDO = sysDeptMapper.selectOne(new LambdaQueryWrapper<SysDeptDO>()
                .eq(SysDeptDO::getId, deptId)
                .select(SysDeptDO::getId, SysDeptDO::getName, SysDeptDO::getParentId, SysDeptDO::getStatus, SysDeptDO::getSort));

        return deptAssembler.toDeptInfoDTO(sysDeptDO);
    }

    /**
     * response 转换
     *
     * @param deptList deptList
     * @return List<DeptResponse>
     */
    private List<DeptDTO> converterDTO(List<SysDeptDO> deptList) {
        List<DeptDTO> list = new ArrayList<>();
        if (CollUtil.isNotEmpty(deptList)) {
            Set<Long> cacheDeptIds = deptList.stream()
                    .map(SysDeptDO::getId)
                    .collect(Collectors.toSet());
            for (SysDeptDO dept : deptList) {
                Long parentId = dept.getParentId();
                // 不在缓存ID列表的parentId是顶级节点ID，以此作为递归开始
                if (!cacheDeptIds.contains(parentId)) {
                    list.addAll(recurDeptList(parentId, deptList));
                    // 避免重复递归
                    cacheDeptIds.add(parentId);
                }
            }
        }
        //  列表为空说明所有的节点都是独立的
        if (list.isEmpty()) {
            return deptList.stream().map(item -> {
                DeptDTO deptDTO = new DeptDTO();
                BeanUtil.copyProperties(item, deptDTO);
                return deptDTO;
            }).toList();
        }
        return list;
    }

    /**
     * 递归生成部门树形列表
     *
     * @param parentId parentId
     * @param deptList deptList
     * @return List<DeptResponse>
     */
    public List<DeptDTO> recurDeptList(Long parentId, List<SysDeptDO> deptList) {
        return deptList.stream()
                .filter(dept -> dept.getParentId().equals(parentId))
                .map(dept -> {
                    DeptDTO deptDTO = deptAssembler.toDeptDTO(dept);
                    List<DeptDTO> children = recurDeptList(dept.getId(), deptList);
                    deptDTO.setChildren(children);
                    return deptDTO;
                }).toList();
    }


    /**
     * 递归生成部门表格层级列表
     *
     * @param parentId parentId
     * @param deptList deptList
     * @return List<OptionResponse < Long>>
     */
    private List<OptionDTO<Long>> recurDeptTreeOptions(long parentId, List<SysDeptDO> deptList) {
        return CollUtil.emptyIfNull(deptList).stream()
                .filter(dept -> dept.getParentId().equals(parentId))
                .map(dept -> {
                    OptionDTO<Long> optionDTO = new OptionDTO<>(dept.getId(), dept.getName());
                    List<OptionDTO<Long>> children = recurDeptTreeOptions(dept.getId(), deptList);
                    if (CollUtil.isNotEmpty(children)) {
                        optionDTO.setChildren(children);
                    }
                    return optionDTO;
                }).toList();
    }

}
